package org.artifact.core.plugin.thread;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.artifact.core.lang.AbstractPlugin;
import org.artifact.core.server.ServerConfig;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.*;

/**
 * thread:
 * # 线程管理插件
 * - name: artifact-main
 * # 逻辑主线程
 * count: 1
 * type:
 * shutdownTimeOut: 180000
 */
public class ThreadPoolPlugin extends AbstractPlugin {

    static final Log log = LogFactory.get(ThreadPoolPlugin.class);

    private Map<String, ThreadPoolConfig> threadPollConfigs = new HashMap<>();

    private Map<String, ExecutorService> executorServices = new HashMap<>();

    public ThreadPoolPlugin() {
        List<Map> threads = ServerConfig.me().deepGet("plugins.thread");
        for (Map config : threads) {
            ThreadPoolConfig threadPoolConfig = BeanUtil.mapToBean(config, ThreadPoolConfig.class, false);
            if (threadPollConfigs.containsKey(threadPoolConfig.getName())) {
                log.error("{} threadPool existed ");
                continue;
            }
            threadPollConfigs.put(threadPoolConfig.getName(), threadPoolConfig);
        }
    }

    public <T extends ExecutorService> T getExecutorService(String threadPoolName) {
        return (T) executorServices.get(threadPoolName);
    }

    @Override
    public boolean start() {
        try {
            for (ThreadPoolConfig threadPoolConfig : threadPollConfigs.values()) {

                ThreadFactory threadFactory = ThreadUtil.newNamedThreadFactory(threadPoolConfig.getName(), false);
                ExecutorService executorService = null;
                switch (threadPoolConfig.getType()) {
                    case "Single":
                        executorService = Executors.newSingleThreadExecutor(threadFactory);
                        break;
                    case "Fixed":
                        executorService = Executors.newFixedThreadPool(threadPoolConfig.getCount(), threadFactory);
                        break;
                    case "Cached":
                        executorService = Executors.newCachedThreadPool(threadFactory);
                        break;
                    case "Scheduled":
                        executorService = Executors.newScheduledThreadPool(threadPoolConfig.getCount(), threadFactory);
                        break;
                    default:
                        log.error("错误的线程池类型");
                        break;

                }
                executorServices.put(threadPoolConfig.getName(), executorService);
            }
        } catch (Exception e) {
            log.error(e);
        }
        return true;
    }

    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        scheduledExecutorService.schedule(() -> {
            Console.log("xxxxxxxxxxxxxxxxxxxxxxxxxxx");
        }, 1, TimeUnit.SECONDS);
        scheduledExecutorService.shutdown();
        scheduledExecutorService.awaitTermination(1000, TimeUnit.MILLISECONDS);
        Console.log("**************************");
    }

    @Override
    public boolean stop() {
        for (Entry<String, ExecutorService> e : executorServices.entrySet()) {
            ThreadPoolConfig threadPoolConfig = threadPollConfigs.get(e.getKey());
            ExecutorService executorService = e.getValue();
            executorService.shutdown();
            try {
                if (!executorService.awaitTermination(threadPoolConfig.getShutdownTimeOut(), TimeUnit.MILLISECONDS)) {
                    log.error("-> {} Shutdown TimeOut", e.getKey());
                    executorService.shutdownNow();
                } else {
                    log.info("-> {} Shutdown Success", e.getKey());
                }
            } catch (InterruptedException ie) {
                log.error("> {} Shutdown TimeOut Error", e.getKey(), ie);
                executorService.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
        return true;
    }
}
